﻿
/******************************************************************************
Module:  AsyncResultNoResult.cs
Notices: Written by Jeffrey Richter

Source: http://msdn.microsoft.com/msdnmag/issues/07/03/ConcurrentAffairs/
 
         Modified by Matthew Cochran Modified by Matthew Cochran (SetAsCompleted() parameter order changed) 

Sample can be found here : http://www.c-sharpcorner.com/uploadfile/rmcochran/multithreadasyncwebservice05262007094719am/multithreadasyncwebservice.aspx?login=true&user=cooldirtyboy

******************************************************************************/

using System;
using System.Threading;

///////////////////////////////////////////////////////////////////////////////

namespace Jerkymon.Service
{
    public class AsyncResultNoResult : IAsyncResult
    {
		#region Static Fields (3)
		private const Int32 c_StateCompletedAsynchronously = 2;
		private const Int32 c_StateCompletedSynchronously = 1;
		// Field set at construction which do change after operation completes
        private const Int32 c_StatePending = 0;
		#endregion

		#region Fields (5)
		// Fields set at construction which never change while operation is pending
        private readonly AsyncCallback m_AsyncCallback;

		// Fields set when operation completes
        private Exception m_exception;

		private Int32 m_CompletedState = c_StatePending;

		private readonly Object m_AsyncState;

		// Field that may or may not get set depending on usage
        private ManualResetEvent m_AsyncWaitHandle;
		#endregion

		#region Properties (4)
		public Object AsyncState
        {
            get { return m_AsyncState; }
        }
		public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (m_AsyncWaitHandle == null)
                {
                    Boolean done = IsCompleted;
                    ManualResetEvent mre = new ManualResetEvent(done);
                    if (Interlocked.CompareExchange(ref m_AsyncWaitHandle, mre, null) != null)
                    {
                        // Another thread created this object's event; dispose the event we just created
                        mre.Close();
                    }
                    else
                    {
                        if (!done && IsCompleted)
                        {
                            // If the operation wasn't done when we created 
                            // the event but now it is done, set the event
                            m_AsyncWaitHandle.Set();
                        }
                    }
                }
                return m_AsyncWaitHandle;
            }
        }
		public Boolean CompletedSynchronously
        {
            get { return m_CompletedState == c_StateCompletedSynchronously; }
        }
		public Boolean IsCompleted
        {
            get { return m_CompletedState != c_StatePending; }
        }
		#endregion

		#region Constructors (1)
		public AsyncResultNoResult(AsyncCallback asyncCallback, Object state)
        {
            m_AsyncCallback = asyncCallback;
            m_AsyncState = state;
        }
		#endregion

		#region Methods (3)

		public void EndInvoke()
        {
            // This method assumes that only 1 thread calls EndInvoke for this object
            if (!IsCompleted)
            {
                // If the operation isn't done, wait for it
                AsyncWaitHandle.WaitOne();
                AsyncWaitHandle.Close();
                m_AsyncWaitHandle = null;  // Allow early GC
            }

            // Operation is done: if an exception occured, throw it
            if (m_exception != null) throw m_exception;
        }

		public void SetAsCompleted(Boolean completedSynchronously)
        {
            SetAsCompleted(completedSynchronously, null);
        }

		public void SetAsCompleted(Boolean completedSynchronously, Exception exception)
        {
            // Passing null for exception means no error occurred; this is the common case
            m_exception = exception;

            // The m_CompletedState field MUST be set prior calling the callback
            Int32 prevState = Interlocked.Exchange(ref m_CompletedState,
               completedSynchronously ? c_StateCompletedSynchronously : c_StateCompletedAsynchronously);
            if (prevState != c_StatePending)
                throw new InvalidOperationException("You can set a result only once");

            // If the event exists, set it
            if (m_AsyncWaitHandle != null) m_AsyncWaitHandle.Set();

            // If a callback method was set, call it
            if (m_AsyncCallback != null) m_AsyncCallback(this);
        }

		#endregion
    }
}
